package com.oj.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.oj.common.core.constants.CacheConstants;
import com.oj.common.core.constants.Constants;
import com.oj.common.core.domain.R;
import com.oj.common.core.domain.TableDataInfo;
import com.oj.common.core.enums.ResultCode;
import com.oj.common.redis.service.RedisService;
import com.oj.common.security.exception.ServiceExpection;
import com.oj.system.domain.exam.Exam;
import com.oj.system.domain.exam.ExamQuestion;
import com.oj.system.domain.exam.dto.*;
import com.oj.system.domain.exam.vo.ExamDetailVO;
import com.oj.system.domain.exam.vo.ExamVO;
import com.oj.system.domain.question.Question;
import com.oj.system.domain.question.vo.QuestionVO;
import com.oj.system.manager.ExamCacheManager;
import com.oj.system.mapper.ExamMapper;
import com.oj.system.mapper.ExamQuestionMapper;
import com.oj.system.mapper.QuestionMapper;
import com.oj.system.service.exam.IExamService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @Description
 * @Author bin
 * @Date 2025/5/6
 */
@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Resource
    private ExamCacheManager examCacheManager;

    @Resource
    private RedisService redisService;

    @Override
    public TableDataInfo list(ExamQueryDTO examQueryDTO, String token) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        List<ExamVO> exams = examMapper.selectExamList(examQueryDTO);
        if (exams == null) {
            return TableDataInfo.empty();
        }
        return TableDataInfo.success(exams, new PageInfo<>(exams).getTotal());
    }

    @Override
    public R<String> add(ExamSaveDTO examSaveDTO) {
        //查重
        ExamValidate(examSaveDTO, null);
        Exam exam = new Exam();
        exam.setTitle(examSaveDTO.getTitle());
        exam.setStartTime(examSaveDTO.getStartTime());
        exam.setEndTime(examSaveDTO.getEndTime());
        //设置为默认不发布模式
        exam.setStatus(Constants.EXAM_UNPUBLISHED);
        int insert = examMapper.insert(exam);
        if (insert <= 0) {
            //数据库插入失败
            throw new ServiceExpection(ResultCode.DATA_INSERT_FAILED);
        }
        //获得id返回 string类型返回
        return R.ok(String.valueOf(exam.getExamId()));
    }

    private void ExamValidate(ExamSaveDTO examSaveDTO, Long examId) {
        List<Exam> exams =
                examMapper.selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId != null, Exam::getExamId, examId)
                );
        if (CollectionUtil.isNotEmpty(exams)) {
            throw new ServiceExpection(ResultCode.FAILED_ALREADY_EXISTS);
        }
        //添加的竞赛开始时间不能小于当前时间
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceExpection(ResultCode.FAILURE);
        }
        //添加的竞赛结束时间不能小于开始时间
        if (examSaveDTO.getEndTime().isBefore(examSaveDTO.getStartTime())) {
            throw new ServiceExpection(ResultCode.FAILURE);
        }
    }

    @Override
    public boolean questionAdd(ExamQuestSaveDTO examQuestSaveDTO) {
        getExam(examQuestSaveDTO.getExamId());
        Set<Long> questionIds = examQuestSaveDTO.getQuestionIdSet();
        List<Question> questions = questionMapper.selectBatchIds(questionIds);
        if (CollectionUtil.isEmpty(questions) || questions.size() < questionIds.size()) {
            throw new ServiceExpection(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        return saveExamQuestionBatch(examQuestSaveDTO, questionIds);
    }

    @Override
    public R<ExamDetailVO> detail(Long examId) {
        Exam exam = getExam(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtil.copyProperties(exam, examDetailVO);

        List<ExamQuestion> examQuestions = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .select(ExamQuestion::getQuestionId)
                .orderByAsc(ExamQuestion::getQuestionOrder)
        );
        if (CollectionUtil.isEmpty(examQuestions)) {
            return R.ok(examDetailVO);
        }
        //将所有题目id放到一个集合里
        Set<Long> questionIds = new HashSet<>();
        for (ExamQuestion examQuestion : examQuestions) {
            questionIds.add(examQuestion.getQuestionId());
        }
        System.out.println(questionIds);
        List<Question> questions = questionMapper.selectBatchIds(questionIds);
        if (CollectionUtil.isEmpty(questions)) {
            throw new ServiceExpection(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        List<QuestionVO> examQuestionList = BeanUtil.copyToList(questions, QuestionVO.class);
        examDetailVO.setExamQuestionList(examQuestionList);
        return R.ok(examDetailVO);
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        //
        if (Constants.EXAM_PUBLISHED.equals(exam.getStatus())) {
            throw new ServiceExpection(ResultCode.EXAM_IS_PUBLISHED);
        }
        //对竞赛标题进行判断重复
        ExamValidate(examEditDTO, exam.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);

    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        //判断竞赛是否开始
        checkExam(exam);
        if (Constants.EXAM_PUBLISHED.equals(exam.getStatus())) {
            throw new ServiceExpection(ResultCode.EXAM_IS_PUBLISHED);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId)
        );
    }

    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        if (Constants.EXAM_PUBLISHED.equals(exam.getStatus())) {
            throw new ServiceExpection(ResultCode.EXAM_IS_PUBLISHED);
        }
        checkExam(exam);
        //删除基础信息
        int res1 = examMapper.deleteById(exam);
        // 检查exam_question表 并删除
        int res2 = examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        return res1 + res2;
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, exam.getExamId()));
        if (count == null || count <= 0) {
            throw new ServiceExpection(ResultCode.EXAM_CANNOT_PUBLISH);
        }
        if (Constants.EXAM_PUBLISHED.equals(exam.getStatus())) {
            //已经发布过了
            throw new ServiceExpection(ResultCode.EXAM_HAS_PUBLISHED);
        }
        exam.setStatus(Constants.EXAM_PUBLISHED);

        // 存到redis中 为提升用户端查询速度
        // key:   e:t:l 未完赛竞赛 value: 中存 examId 的list
        // key:   e:h:l  历史竞赛  value: 中存 examId 的list
        // 竞赛的基本信息只储存一份 key: e:d:examId value: 储存每个竞赛信息的json形式

        examCacheManager.addCache(exam);

        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if (Constants.EXAM_UNPUBLISHED.equals(exam.getStatus())) {
            //未发布
            throw new ServiceExpection(ResultCode.EXAM_NOT_PUBLISHED);
        }
        exam.setStatus(Constants.EXAM_UNPUBLISHED);

        // 将redis中存储的信息删除
        examCacheManager.deleteCache(examId);
        // 删除redis中题目顺序表
        redisService.deleteObject(CacheConstants.QUESTION_LIST);

        return examMapper.updateById(exam);
    }

    private boolean saveExamQuestionBatch(ExamQuestSaveDTO examQuestSaveDTO, Set<Long> questionIds) {
        int num = 1;
        List<ExamQuestion> examQuestions = new ArrayList<>();
        for (Long questionId : questionIds) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setQuestionId(questionId);
            examQuestion.setExamId(examQuestSaveDTO.getExamId());
            examQuestion.setQuestionOrder(num++);
            examQuestions.add(examQuestion);
        }
        //群组插入
        return saveBatch(examQuestions);
    }

    private static void checkExam(Exam exam) {
        //判断竞赛是否开始
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            //竞赛已经开始了
            throw new ServiceExpection(ResultCode.EXAM_CANNOT_OPERATE);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceExpection(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }


}
